#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <asynio.h>
#include <log.h>

AsynManager *asyn_io_create(int max_fds)
{
    AsynManager *m = new AsynManager();

    if (max_fds <= 0)
        max_fds = 8192;

    m->max_accept_fds = max_fds;

    m->epfd = epoll_create(max_fds);

    if (m->epfd < 0) {
        log_print("asyn_io_create: epoll_create failed: %s\n",
                  strerror(errno));
        delete m;
        return NULL;
    }

    return m;
}

void asyn_io_destroy(AsynManager *m)
{
    if (m->epfd >= 0)
        close(m->epfd);

    delete m;
}

int asyn_io_add(AsynManager *m, int fd, int events)
{
    FdRegMap::iterator iter = m->reg_map.find(fd);

    if (iter != m->reg_map.end()) {
        log_print("asyn_io_add: %d has exists\n", fd);
        return -1;
    }

    if (m->reg_map.size() >= m->max_accept_fds) {
        log_print("asyn_io_add: touch max %d fds\n",
                  m->max_accept_fds);
        return -1;
    }

    if ((events & ~(EVT_TYPE_IN | EVT_TYPE_OUT))
        || events == 0) {
        log_print("asyn_io_add: invalid events flags %d\n", events);
        return -1;
    }

    struct epoll_event evt;

    int flags = 0;
    if (events & EVT_TYPE_IN)
        flags |= EPOLLIN;
    if (events & EVT_TYPE_OUT)
        flags |= EPOLLOUT;

    evt.events  = flags;
    evt.data.fd = fd;

    if (epoll_ctl(m->epfd, EPOLL_CTL_ADD, fd, &evt) < 0) {
        log_print("asyn_io_add: epoll_ctl %d failed: %s\n",
                  fd, strerror(errno));
        return -1;
    }

    EvtItem item;

    item.last_update_ticks = get_ticks();
    item.events            = flags;

    m->reg_map.insert(FdRegMap::value_type(fd, item));

    return 0;
}

int asyn_io_del(AsynManager *m, int fd)
{
    FdRegMap::iterator iter = m->reg_map.find(fd);

    if (iter == m->reg_map.end()) {
        log_print("asyn_io_del: %d not register\n", fd);
        return -1;
    }

    struct epoll_event evt;
    evt.events = iter->second.events;
    evt.data.fd = fd;

    m->reg_map.erase(iter);

    if (epoll_ctl(m->epfd, EPOLL_CTL_DEL, fd, &evt) < 0) {
        log_print("asyn_io_del: epoll_ctl %d failed: %s\n",
                  fd, strerror(errno));
        return -1;
    }

    return 0;
}

int asyn_io_test(int argc, char **argv)
{
    AsynManager *m = asyn_io_create(1024);

    asyn_io_add(m, 0, EVT_TYPE_IN);

    bool quit = false;

    while (!quit) {

        ASYN_IO_BEG(m, 0) {
            int fd = ASYN_IO_THIS_FD;

            printf("ok to get data\n");
            char buf[1024];
            int r = read(fd, buf, 1024);
            printf("read return %d\n", r);
            printf("putting...\n");
            if (r > 0) {
                write(1, buf, r);
                if (buf[0] == 'q')
                    quit = true;
            }

            ASYN_IO_UPDATE_TICKS(m);
        }

        ASYN_IO_END;

        ASYN_IO_TIMEOUT_BEG(m, (1000 * 30)) {
            int fd = ASYN_IO_TIMEOUT_THIS_FD;

            printf("%d timeout\n", fd);

            quit = true;
        }
        ASYN_IO_TIMEOUT_END;
    }

    return 0;
}
